package com.shbaiche.bifenba.net;

import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.codec.Hex;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class DigestAuthUtils
{

	public static String createDigestAuthorization(String uri, String userName, String realName, String password,
			String digestKey, String httpMethod) throws IllegalArgumentException
	{
		int nonceValiditySeconds = 3600;
		long expiryTime = System.currentTimeMillis() + (nonceValiditySeconds * 1000);
		String signatureValue = new String(md5Hex(expiryTime + ":" + digestKey));
		String nonceValue = expiryTime + ":" + signatureValue;
		String nonceValueBase64 = new String(Base64.encode(nonceValue.getBytes()));
		String generateDigest = DigestAuthUtils.generateDigest(false, userName, realName, password, httpMethod, uri,
				null, nonceValueBase64, null, null);
		String authenticateHeader = "Digest username=\"" + userName + "\", realm=\"" + realName + "\", response=\""
				+ generateDigest + "\", uri=\"" + uri + "\", " + "nonce=\"" + nonceValueBase64 + "\"";
		return authenticateHeader;
	}

	public static String encodePasswordInA1Format(String username, String realm, String password)
	{
		String a1 = username + ":" + realm + ":" + password;
		String a1Md5 = md5Hex(a1);

		return a1Md5;
	}

	/**
	 * Computes the <code>response</code> portion of a Digest authentication header. Both the server and user agent
	 * should compute the <code>response</code> independently. Provided as a static method to simplify the coding of
	 * user agents.
	 * @param passwordAlreadyEncoded true if the password argument is already encoded in the correct format. False if it
	 *        is plain text.
	 * @param username the user's login name.
	 * @param realm the name of the realm.
	 * @param password the user's password in plaintext or ready-encoded.
	 * @param httpMethod the HTTP request method (GET, POST etc.)
	 * @param uri the request URI.
	 * @param qop the qop directive, or null if not set.
	 * @param nonce the nonce supplied by the server
	 * @param nc the "nonce-count" as defined in RFC 2617.
	 * @param cnonce opaque string supplied by the client when qop is set.
	 * @return the MD5 of the digest authentication response, encoded in hex
	 * @throws IllegalArgumentException if the supplied qop value is unsupported.
	 */
	public static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
			String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
			throws IllegalArgumentException
	{
		String a1Md5 = null;
		String a2 = httpMethod + ":" + uri;
		String a2Md5 = md5Hex(a2);

		if (passwordAlreadyEncoded)
		{
			a1Md5 = password;
		}
		else
		{
			a1Md5 = DigestAuthUtils.encodePasswordInA1Format(username, realm, password);
		}

		String digest;

		if (qop == null)
		{
			// as per RFC 2069 compliant clients (also reaffirmed by RFC 2617)
			digest = a1Md5 + ":" + nonce + ":" + a2Md5;
		}
		else if ("auth".equals(qop))
		{
			// As per RFC 2617 compliant clients
			digest = a1Md5 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + a2Md5;
		}
		else
		{
			throw new IllegalArgumentException("This method does not support a qop: '" + qop + "'");
		}

		String digestMd5 = new String(md5Hex(digest));

		return digestMd5;
	}

	public static String md5Hex(String data)
	{
		MessageDigest digest;
		try
		{
			digest = MessageDigest.getInstance("MD5");
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new IllegalStateException("No MD5 algorithm available!");
		}

		return new String(Hex.encode(digest.digest(data.getBytes())));
	}
}
